<script setup lang="ts">
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'
import {
  AxesHelper,
  Scene,
  PerspectiveCamera,
  WebGLRenderer,
  TextureLoader,
  BufferGeometry,
  BufferAttribute,
  AdditiveBlending,
  Points,
  PointsMaterial,
  Color,
  HexColorString,
} from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GUI } from 'dat.gui'
import Stats from 'three/examples/jsm/libs/stats.module'
import starPng from '../images/5.png'

const contentRef = ref<HTMLCanvasElement>()
const renderer = shallowRef<WebGLRenderer>(new WebGLRenderer())
const orbitControls = shallowRef<OrbitControls>()
const scene = new Scene()
const camera = shallowRef<PerspectiveCamera>()
const axesHelper = new AxesHelper(20)
// 创建性能监视器
let stats = new Stats()

const gui = new GUI()

const init = () => {
  if (contentRef.value) {
    camera.value = new PerspectiveCamera(75, contentRef.value.clientWidth / contentRef.value.clientHeight)
    camera.value.position.set(0, 10, 20)
    scene.add(camera.value)
    scene.add(axesHelper)
    contentRef.value.appendChild(renderer.value.domElement)

    stats.dom.style.position = 'absolute'
    stats.dom.style.top = '0'
    stats.dom.style.left = '0'
    contentRef.value.appendChild(stats.dom)

    renderer.value.setSize(contentRef.value?.clientWidth, contentRef.value?.clientHeight)

    orbitControls.value = new OrbitControls(camera.value, renderer.value.domElement)
    orbitControls.value.enableDamping = true
  }
}

const render = () => {
  if (camera.value) {
    orbitControls.value?.update()
    renderer.value?.render(scene, camera.value)
  }
  stats.update()
  requestAnimationFrame(render)
}

window.addEventListener('resize', () => {
  if (contentRef.value && camera.value) {
    camera.value.aspect = contentRef.value.clientWidth / contentRef.value.clientHeight
    camera.value.updateProjectionMatrix()

    renderer.value.setSize(contentRef.value?.clientWidth, contentRef.value?.clientHeight)
    renderer.value.setPixelRatio(window.devicePixelRatio)
  }
})

onMounted(() => {
  init()

  const guiAction = {
    count: 10000,
    size: 0.5,
    radius: 20,
    branch: 4,
    color: '#ff6030' as HexColorString,
    endColor: '#1b3984' as HexColorString,
  }

  const textureLoader = new TextureLoader()
  const texture = textureLoader.load(starPng)
  const bufferGeometry = new BufferGeometry()
  const buffer = new Float32Array(guiAction.count * 3)
  const color = new Float32Array(guiAction.count * 3)

  const startColor = new Color(guiAction.color)
  const endColor = new Color(guiAction.endColor)

  for (let i = 0; i < guiAction.count; i++) {
    const branchAngel = ((i % guiAction.branch) * (2 * Math.PI)) / guiAction.branch

    const radius = Math.random() * guiAction.radius * Math.pow(Math.random(), 3)

    const i3 = i * 3

    const randomX = (Math.pow(Math.random() * 2 - 1, 3) * (guiAction.radius - radius)) / 10
    const randomY = (Math.pow(Math.random() * 2 - 1, 3) * (guiAction.radius - radius)) / 10
    const randomZ = (Math.pow(Math.random() * 2 - 1, 3) * (guiAction.radius - radius)) / 10

    buffer[i3] = Math.sin(branchAngel + radius * 0.1) * radius + randomX * 2
    buffer[i3 + 1] = 0 + randomY * 2
    buffer[i3 + 2] = Math.cos(branchAngel + radius * 0.1) * radius + randomZ * 2

    const co = startColor.lerp(endColor, (guiAction.radius - radius) / 10)
    color[i3] = co.r
    color[i3 + 1] = co.g
    color[i3 + 2] = co.b
  }
  bufferGeometry.setAttribute('position', new BufferAttribute(buffer, 3))
  bufferGeometry.setAttribute('color', new BufferAttribute(color, 3))
  const points = new Points(
    bufferGeometry,
    new PointsMaterial({
      map: texture,
      alphaMap: texture,
      transparent: true,
      depthWrite: false,
      blending: AdditiveBlending,
      size: guiAction.size,
      vertexColors: true,
    })
  )
  scene.add(points)

  render()
})

onUnmounted(() => {
  gui.destroy()
})
</script>

<template>
  <div class="star" ref="contentRef"></div>
</template>

<style lang="scss" scoped>
.star {
  width: 100%;
  height: 100%;
  position: relative;
}
</style>
